package com.hzlj.position.dbsync.guangdong.rest;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fz.common.base.core.FileDownloadUtil;
import com.fz.common.base.core.JsonUtils;
import com.fz.common.base.exception.PubException;
import com.hzlj.position.config.common.dto.jzJbxxDeviceBind.JzJbxxDeviceBindSimpleQueryParamDTO;
import com.hzlj.position.config.common.enums.Channel;
import com.hzlj.position.config.common.enums.ChannelType;
import com.hzlj.position.config.common.enums.WarningType;
import com.hzlj.position.dbsync.guangdong.config.GuangdongLocateSyncConfig;
import com.hzlj.position.locate.common.common.PositionWithExtDTO;
import com.hzlj.position.locate.common.dto.position.PositionExtDTO;
import com.hzlj.position.locate.common.dto.position.PositionQueryParamDTO;
import com.hzlj.position.locate.common.dto.position.PositionSyncDTO;
import com.hzlj.position.locate.common.enums.TbztStatus;
import com.hzlj.position.locate.dao.PositionDao;
import com.hzlj.position.locate.service.PositionService;
import com.hzlj.sqjz.config.common.dto.file.FileQueryResultDTO;
import com.hzlj.sqjz.config.service.FileService;
import com.hzlj.sqjz.jz.common.dto.common.JzSimpleQueryResultDTO;
import com.hzlj.sqjz.jz.service.JzJbxxService;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class GuangdongLocateSyncRest {
    @Resource
    private GuangdongLocateSyncConfig guangdongLocateSyncConfig;
    @Resource
    private PositionDao positionDao;
    @Resource
    private PositionService positionService;
    @Resource
    private JzJbxxService jbxxService;
    @Resource
    private FileService fileService;
    @Resource
    private RedisTemplate<String, Date> redisTemplate;
    private final static String OPERATOR_KEY = "guangdong:sync:operator";
    private final static String APP_KEY = "guangdong:sync:app";
    //定位数据推送接口
    private final static String pubDwjlNew = "/sqjz/dzjg/dwjl/sync/pubDwjlNew";
    //信息化核查数据推送接口
    private final static String thirdPartyClockIn = "/sqjz/zjt/onlineSignIn/thirdPartyClockIn";
    //异常定位数据推送接口
    private final static String pubYcDwjl = "/sqjz/dzjg/dwjl/sync/pubYcDwjl";


    /**
     * 同步运营商数据
     */
    public int syncOperator() {
        Date now = DateUtil.parseDateTime(DateUtil.formatDateTime(new Date()));
        //获取最近的记录
        PositionQueryParamDTO positionParams = new PositionQueryParamDTO()
                .setChannelType(ChannelType.MOBILE.getCode())
                .setSyncStatus(TbztStatus.WAIT.getStatus())
                .setWarning(false);
        try {
            List<PositionWithExtDTO> positions = this.positions(OPERATOR_KEY, now, positionParams);
            return syncOperator(positions);
        } finally {
            //redis同步时间
            redisTemplate.opsForValue().set(OPERATOR_KEY, now);
        }
    }


    /**
     * 同步运营商数据
     */
    public int syncOperator(List<PositionWithExtDTO> positions) {
        if (ObjectUtil.isEmpty(positions)) {
            log.info("【广东数据同步】定位数据推送,没有新的数据");
            return 0;
        }
        //数据转换
        List<SyncOperatorParams.EventDetailDTO> details = positions.stream()
                .map(e -> {
                    SyncOperatorParams.EventDetailDTO detail = new SyncOperatorParams.EventDetailDTO();
                    detail.setName(e.getXm());
                    detail.setPhone(e.getDeviceCode());
                    detail.setFixedtime(DateUtil.format(e.getPositionTime(), "yyyy-MM-dd HH:mm:ss"));
                    detail.setAddress(e.getPosition());
                    detail.setLat(e.getLatitude());
                    detail.setLng(e.getLongitude());
                    //定位来源：1.基站，2.GPS/北斗，3微信小程序
                    detail.setSourcebase("1");
                    //终端电量：带%号，如30%，无数据则默认100%
                    detail.setDevicevoltage("100%");
                    //终端状态：1正常，2无信号，3关机
                    detail.setDevicestatus(deviceStatus(e));
                    detail.setSource(getSource(e));
                    return detail;
                }).collect(Collectors.toList());

        SyncOperatorParams params = new SyncOperatorParams();
        params.setBatchtime(DateUtil.format(new Date(), "yyyyMMddHHmmss"));
        params.setEventDetail(details);

        //推送
        boolean success = true;
        String syncMsg = "同步成功";
        try {
            execute(pubDwjlNew, "定位数据推送", params);
        } catch (PubException e) {
            success = false;
            syncMsg = e.getMsg();
        } finally {
            updateSyncStatus(positions, success, syncMsg);
        }
        return positions.size();
    }

    /**
     * 同步app数据
     */
    public int syncApp() {
        Date now = new Date();
        //获取最近的记录
        PositionQueryParamDTO positionParams = new PositionQueryParamDTO()
                .setChannelType(ChannelType.APP.getCode())
                .setSyncStatus(TbztStatus.WAIT.getStatus())
                .setWarning(false);
        List<PositionWithExtDTO> positions = this.positions(APP_KEY, now, positionParams);
        return this.syncApp(positions);
    }

    /**
     * 同步app数据
     */
    public int syncApp(List<PositionWithExtDTO> positions) {
        if (ObjectUtil.isEmpty(positions)) {
            log.info("【广东数据同步】信息化核查数据推送,没有新的数据");
            return 0;
        }
        List<JzSimpleQueryResultDTO> jzJbxxs = jbxxService.listJzJbxxSimple(new JzJbxxDeviceBindSimpleQueryParamDTO().setJzIds(
                positions.stream().map(PositionWithExtDTO::getJzId).collect(Collectors.toList())
        ));

        Map<String, JzSimpleQueryResultDTO> jzJbxxsByJzId = jzJbxxs.stream().collect(Collectors.toMap(JzSimpleQueryResultDTO::getJzId, e -> e));

        for (PositionWithExtDTO position : positions) {
            JzSimpleQueryResultDTO jzJbxx = jzJbxxsByJzId.get(position.getJzId());
            try {
                SyncAppParams params = new SyncAppParams();
                params.setUuid(IdUtil.fastSimpleUUID());
                params.setLongitude(position.getLongitude());
                params.setLatitude(position.getLatitude());
                params.setAddress(position.getPosition());
                params.setPhoneNum(jzJbxx != null ? jzJbxx.getSjh() : null);
                params.setFaceImg(getFaceImg(position));
                params.setSource(getSource(position));
                params.setSourcebase("2");
                params.setDevicevoltage("100%");
                params.setDevicestatus(deviceStatus(position));
                //推送
                execute(thirdPartyClockIn, "信息化核查数据推送", params);
            } catch (Exception e) {
                log.error("【广东数据同步】发起请求失败", e);
            }
        }

        //redis同步时间
        if (ObjectUtil.isNotEmpty(positions)) {
            Date endTime = positions.get(positions.size() - 1).getPositionTime();
            redisTemplate.opsForValue().set(OPERATOR_KEY, endTime);
        }
        return positions.size();
    }

    /**
     * 同步异常数据
     */
    public int syncWarning() {
        return 0;
    }

    /**
     * 同步异常数据
     */
    public int syncWarning(List<PositionWithExtDTO> positions) {
        return 0;
    }


    /**
     * 更新同步状态
     */
    private void updateSyncStatus(List<PositionWithExtDTO> positions,
                                  boolean success,
                                  String syncMsg) {
        positionService.updateSync(new PositionSyncDTO()
                .setPositions(positions)
                .setSyncTime(new Date())
                .setSyncMsg(syncMsg)
                .setSyncStatus(success ? "1" : "2")
        );
    }


    /**
     * 头像
     */
    private String getFaceImg(PositionWithExtDTO position) {
        PositionExtDTO ext = position.getPositionExt();
        if (ObjectUtil.isEmpty(ext) || ObjectUtil.isEmpty(ext.getFileIds())) {
            return null;
        }
        //文件信息
        FileQueryResultDTO file = fileService.getFileById(ext.getFileIds());
        return FileDownloadUtil.download(file.getFullUrl());
    }

    /**
     * 签名
     */
    private String getSignature(CommonParams params) {
        String key = guangdongLocateSyncConfig.getKey();
        return getSHA256(params.getTimestamp() + key + params.getNonce() + params.getTimestamp());
    }

    /**
     * 终端状态：1正常，2无信号，3关机
     */
    private String deviceStatus(PositionWithExtDTO position) {
        if (position.getWarningType().equals(WarningType.POWER_OFF.getCode())) {
            return "3";
        }
        if (ObjectUtil.hasEmpty(position.getLatitude(), position.getLongitude())) {
            return "2";
        }
        return "1";
    }

    /**
     * 运营商：1.移动，2.联通，3.电信，4.微信小程序
     */
    private String getSource(PositionWithExtDTO position) {
        String channel = position.getChannel();
        switch (Channel.of(channel)) {
            case MOBILE:
                return "1";
            case UNICOM:
                return "2";
            case TELECOM:
                return "3";
            case APP:
                return "6";
            default:
                return null;
        }
    }


    private List<PositionWithExtDTO> positions(String key, Date now, PositionQueryParamDTO params) {
        Date startTime = redisTemplate.opsForValue().get(key);
        if (startTime == null) {
            startTime = DateUtil.offsetMinute(now, -10);
        }
        params
                .setJgCode(guangdongLocateSyncConfig.getJgCode())
                .setStartPositionTime(startTime)
                .setEndPositionTime(now);
        return this.positionDao.listPositionWithExt(params);
    }


    private <T extends CommonParams> void execute(String uri,
                                                  String buss,
                                                  T params) {
        params.setTimestamp(DateUtil.format(new Date(), "yyyyMMddHHmmss"));
        params.setNonce(RandomUtil.randomString(16));
        params.setUsername(guangdongLocateSyncConfig.getUsername());
        params.setSignature(this.getSignature(params));

        log.info("【广东数据同步】设置数据({})：url={},params={}", buss, guangdongLocateSyncConfig.getUrl() + uri, params);
        HttpRequest request = HttpUtil.createPost(guangdongLocateSyncConfig.getUrl() + uri)
                .body(JsonUtils.obj2json(params));
        try (HttpResponse execute = request.execute()) {
            String body = execute.body();
            log.info("【广东数据同步】设置数据结果({})：params={},result={}", buss, params, body);
            SyncResult result = JsonUtils.json2obj(body, SyncResult.class);
            if (result.success()) {
                return;
            }
            throw new PubException(result.msg);
        } catch (PubException e) {
            throw e;
        } catch (Exception e) {
            log.error("【广东数据同步】发起请求失败({})：params={}", buss, params, e);
            throw new PubException("发起同步请求失败");
        }
    }


    @NoArgsConstructor
    @Data
    public static class CommonParams {
        @JsonProperty("username")
        private String username;
        @JsonProperty("signature")
        private String signature;
        @JsonProperty("timestamp")
        private String timestamp;
        @JsonProperty("nonce")
        private String nonce;
    }

    @NoArgsConstructor
    @Data
    public static class SyncOperatorParams extends CommonParams {
        @JsonProperty("batchtime")
        private String batchtime;
        @JsonProperty("event_detail")
        private List<EventDetailDTO> eventDetail;

        @NoArgsConstructor
        @Data
        public static class EventDetailDTO {
            @JsonProperty("name")
            private String name;
            @JsonProperty("phone")
            private String phone;
            @JsonProperty("fixedtime")
            private String fixedtime;
            @JsonProperty("address")
            private String address;
            @JsonProperty("lat")
            private String lat;
            @JsonProperty("lng")
            private String lng;
            @JsonProperty("sourcebase")
            private String sourcebase;
            @JsonProperty("devicevoltage")
            private String devicevoltage;
            @JsonProperty("devicestatus")
            private String devicestatus;
            @JsonProperty("source")
            private String source;
        }

        @Override
        public String toString() {
            return "SyncOperatorParams{" +
                    "batchtime='" + batchtime + '\'' +
                    ", username='" + super.getUsername() + '\'' +
                    ", signature='" + super.getSignature() + '\'' +
                    ", timestamp='" + super.getTimestamp() + '\'' +
                    ", nonce='" + super.getNonce() + '\'' +
                    '}';
        }
    }


    @Data
    public static class SyncAppParams extends CommonParams {
        @JsonProperty("uuid")
        private String uuid;
        @JsonProperty("longitude")
        private String longitude;
        @JsonProperty("latitude")
        private String latitude;
        @JsonProperty("address")
        private String address;
        @JsonProperty("phoneNum")
        private String phoneNum;
        @JsonProperty("faceImg")
        private String faceImg;
        @JsonProperty("source")
        private String source;
        @JsonProperty("sourcebase")
        private String sourcebase;
        @JsonProperty("devicevoltage")
        private String devicevoltage;
        @JsonProperty("devicestatus")
        private String devicestatus;

        @Override
        public String toString() {
            return JsonUtils.obj2json(this);
        }
    }

    @Data
    @NoArgsConstructor
    public static class SyncResult {
        @JsonProperty("code")
        private String code;
        @JsonProperty("msg")
        private String msg;

        public boolean success() {
            return "200".equals(code);
        }
    }

    @Data
    public static class SyncPosition {
        private Date positionTime;
    }


    public String getSHA256(String str) {
        MessageDigest messageDigest;
        String encodestr = "";
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(str.getBytes(StandardCharsets.UTF_8));
            encodestr = byte2Hex(messageDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return encodestr;
    }

    private String byte2Hex(byte[] bytes) {
        StringBuilder builder = new StringBuilder();
        String temp;
        for (byte aByte : bytes) {
            temp = Integer.toHexString(aByte & 0xFF);
            if (temp.length() == 1) {
                //1得到一位的进行补0操作
                builder.append("0");
            }
            builder.append(temp);
        }
        return builder.toString();
    }
}
